package com.slunk.platforms;

//import java.util.HashMap;
import java.util.Map;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.Timer.Task;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
//import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.slunk.platforms.SOPCodes;
import com.slunk.platforms.SRenderSprite;

public class SActingObject extends SRenderSprite {
	class SoundObject
	{
		public Sound sound;
		public Long soundId;
		public SoundObject(Sound sound, Long soundId) {
			this.sound = sound;
			this.soundId = soundId;
		}
	}
	
	private boolean currentStateObserved = false;
	private boolean isDelayed = false;
	private boolean justTapped = false;
	private Timer timer;
	private Timer soundTimer;
	private String delayedState = null;
	private String delayedOpCode = null;
	private String delimitter = "!";
	private SActingObjectEvents eventHandler;
	private Array<SoundObject> soundArray;
	public Rectangle hotArea = null;
	
	/**
	 * The current state.
	 * 
	 * Provides an index into the stateMachine array. 
	 */
	private int currentState;
	
	/**
	 * Composes the different states an SActingObject can be in.
	 * 
	 * Currently the SActingObject
	 */
	private Array<Map<SOPCodes, String>> stateMachine;
	
	/**
	 * Parent texture texture atlas needed if the op code specifies a new image to be displayed
	 */
	TextureAtlas parentTextureAtlas;
	
	//Not all active objects will have the same sprite layout. Some active objects might have an accompanying image, like an alternate image.
	//Others may simply have a single image and no counterpart.
	//public 
	public SActingObject(TextureAtlas parentTextureAtlas, Map<SOPCodes, String> firstState) {
		//not sure what to do here yet
		super();
		this.parentTextureAtlas = parentTextureAtlas;
		currentState = -1;
		stateMachine = new Array<Map<SOPCodes,String>>();
		soundArray = new Array<SoundObject>();
		stateMachine.add(firstState);
		
		//logically need to activate the next state to proceed
		activateNextState();
		
		hotArea = new Rectangle(this.getSprite().getBoundingRectangle());
		eventHandler = null;
		isDelayed = false;
	}
	public void setEventHandler(SActingObjectEvents eventHandler) {
		this.eventHandler = eventHandler;
	}
	public SActingObjectEvents getEventHandler() {
		return eventHandler;
	}
	public boolean isPointHot(float screenX, float screenY) {
		if (hotArea.contains(screenX, screenY)) {
			return true;
		}
		return false;
	}
	public void setHotArea(Rectangle hotArea) {
		this.hotArea = new Rectangle(hotArea);
	}
	public void addState(Map<SOPCodes,String> state) {
		stateMachine.add(state);
	}
	public boolean hasMoreStates() {
		if (currentState + 1 < stateMachine.size) {
			return true;
		}
		return false;
	}
	public void activateStateAt(int index) {
		if (index < stateMachine.size || index >= 0) {
			if (soundArray.size > 0) {
				for (SoundObject soundObject : soundArray) {
					soundObject.sound.stop(soundObject.soundId);
				}
				soundArray.clear();
			}
			currentStateObserved = false;
			currentState = index;
			for (SOPCodes opCode : stateMachine.get(currentState).keySet()){
				if (opCode != SOPCodes.OP_ONTAP) {
					resolveOpCode(opCode,stateMachine.get(currentState).get(opCode));
				}
			}
		}
	}
	public void activatePreviousState() {
		if (currentState - 1 > -1) {
			activateStateAt(currentState-1);
		} else {
			System.out.println("no more previou states");
		}
		if (eventHandler != null && !isDelayed) {
			eventHandler.onChangedState(this);
		}
	}
	public void activateNextState() {
		if (currentState + 1 < stateMachine.size) {
			activateStateAt(currentState + 1);
		} else {
			//System.out.println("ran out of states");
		}
		if (eventHandler != null && !isDelayed) {
			eventHandler.onChangedState(this);
		}
	}
	public boolean getCurrentStateObserved() {
		return currentStateObserved;
	}
	public String tapAndObserve() {
		tap();
		return observerCurrentState();
	}
	public boolean tap() {
		if(stateMachine.get(currentState).containsKey(SOPCodes.OP_ONTAP)) {
			resolveOpCode(SOPCodes.OP_ONTAP, stateMachine.get(currentState).get(SOPCodes.OP_ONTAP));
			return true;
		} 
		return false;
	}
	
	public boolean hasTapState() {
		if (stateMachine.get(currentState).containsKey(SOPCodes.OP_ONTAP)) {
			return true;
		}
		return false;
	}
	
	
	public boolean hasQuestion() {
		if(stateMachine.get(currentState).containsKey(SOPCodes.OP_QUESTION)) {
			return true;
		}
		return false;
	}
	public boolean hasDescription() {
		if(stateMachine.get(currentState).containsKey(SOPCodes.OP_DESCRIPTION)) {
			return true;
		}
		return false;
	}
	
	
	public String observerCurrentState() {
		currentStateObserved = true;
		return getDescription();
	}
	
	public String getDescription() {
		if(hasDescription()) {
			return stateMachine.get(currentState).get(SOPCodes.OP_DESCRIPTION);
		} else if (hasQuestion()) {
			return stateMachine.get(currentState).get(SOPCodes.OP_QUESTION);
		}
		return null;
	}
	
	public void setPosition(int screenX, int screenY) {
		super.setPosition(screenX,screenY);
		this.setHotArea(this.getSprite().getBoundingRectangle());
	}
	
	public String[] splitValue(String value) {
		return value.split(delimitter);
	}
	
	
	/**
	 * Use this to resolve and op code and its value.
	 * @param opCode
	 * @param value
	 */
	private void resolveOpCode(SOPCodes opCode, String value) {
		//System.out.println(opCode.toString());
		switch (opCode){
			case OP_DESCRIPTION:
				break;
			case OP_IMAGE:
				setSpriteRegion(parentTextureAtlas.findRegion(value));
				break;
			case OP_POSITION_XY:
				String[] positionxyParts = splitValue(value);//value.split(delimitter);
				if (positionxyParts.length != 2) {
					throw new GdxRuntimeException("OP_POSITION_XY: Invalid value");
				}
				this.getSprite().setPosition(Integer.parseInt(positionxyParts[0]), Integer.parseInt(positionxyParts[1]));
				break;
			case OP_POSITION_X:
				this.getSprite().setPosition(Integer.parseInt(value), this.getSprite().getY());
				break;
			case OP_POSITION_Y:
				this.getSprite().setPosition(this.getSprite().getX(),Integer.parseInt(value));
				break;
			case OP_SCALE_XY:
				this.getSprite().setScale(Float.parseFloat(value));
				break;
			case OP_SCALE_X:
				this.getSprite().setScale(Float.parseFloat(value), this.getSprite().getScaleY());
				break;
			case OP_SCALE_Y:
				this.getSprite().setScale(this.getSprite().getScaleX(),Float.parseFloat(value));
				break;
			case OP_ROTATE:
				this.getSprite().setRotation(Float.parseFloat(value));
				break;
			case OP_ANIMATION:
				String[] animationParts = splitValue(value);
				if (animationParts.length != 4) {
					throw new GdxRuntimeException("OP_ANIMATION: Invalid value");
				}
				String textureRegion = animationParts[0];
				Float duration = Float.parseFloat(animationParts[1]);
				Boolean looping = Boolean.parseBoolean(animationParts[2]);
				Integer playMode = Integer.parseInt(animationParts[3]);
				this.setAnimation(parentTextureAtlas.findRegions(textureRegion),
						duration, 
						looping,
						playMode);
				break;
			case OP_DELAY:;
				String[] delayedParts = splitValue(value);
				if (delayedParts.length != 3) {
					throw new GdxRuntimeException("OP_DELAY: Invalid value");
				}
				isDelayed = true;
				delayedOpCode = delayedParts[1];
				delayedState = delayedParts[2];
				int seconds = Integer.parseInt(delayedParts[0]);
				timer = new Timer();
				timer.scheduleTask(new Task() {  
		            @Override  
		            public void run() { 
		            	if (!delayedOpCode.equals("OP_SOUND")) {
		            		isDelayed = false;
		            	}
		            	executeStateJson(delayedOpCode,delayedState);
		            }  
		        }, seconds);
				break;
			case OP_SOUND:
				//lets have it be composed of path to sound, volume, pitch, pan, and looping
				String[] soundParts = splitValue(value);
				Sound sound = Gdx.audio.newSound(Gdx.files.internal(soundParts[0]));
				long soundId;
				if (Boolean.parseBoolean(soundParts[4])) {
					soundId = sound.loop(Float.parseFloat(soundParts[1]));
				} else {
					soundId = sound.play(Float.parseFloat(soundParts[1]), Float.parseFloat(soundParts[2]), Float.parseFloat(soundParts[3]));
				}
				soundArray.add(new SoundObject(sound,soundId));
				soundTimer = new Timer();
				soundTimer.scheduleTask(new Task() {  
		            @Override  
		            public void run() {
		            	isDelayed = false;
		            	if (eventHandler != null) {
		        			eventHandler.onChangedState(this);
		        		}
		            }
		        }, Float.parseFloat(soundParts[5]));
				
				break;
			case OP_NEXT_STATE:
				activateNextState();
				break;
			case OP_SACTINGOBJECT:
				//creates whole new SActingObject
				break;
			case OP_ONTAP:
			case OP_INSTANT:
				//occurs instantly
				//System.out.println("instant action");
				String[] tappedParts = splitValue(value);			
				executeStateJson(tappedParts[0],tappedParts[1]);
				break;
			default:
				break;
		}
	}
	
	private void executeStateJson(String opCodeString, String value) {
		Json json = new Json();
		if (opCodeString.equals("OP_ANIMATION")) {
        	SAnimationStateJson animatingObject = json.fromJson(SAnimationStateJson.class, value);
        	SOPCodes opCode = SOPCodes.valueOf(animatingObject.opCode);
        	String constructedStateString = animatingObject.animationName
        				+delimitter+animatingObject.frameDuration.toString()
        				+delimitter+animatingObject.looping.toString()
        				+delimitter+animatingObject.playmode.toString();
        	resolveOpCode(opCode,constructedStateString);
    	} else if (opCodeString.equals("OP_SOUND")) {
    		SSoundStateJson soundObject = json.fromJson(SSoundStateJson.class, value);
        	SOPCodes opCode = SOPCodes.valueOf(soundObject.opCode);
        	String constructedStateString = soundObject.path
        				+delimitter+soundObject.volume.toString()
        				+delimitter+soundObject.pitch.toString()
        				+delimitter+soundObject.pan.toString()
        				+delimitter+soundObject.looping.toString()
        				+delimitter+soundObject.length.toString();
        	resolveOpCode(opCode,constructedStateString);
    	} else if (opCodeString.equals("OP_IMAGE")) {
    		SOPCodes opCode = SOPCodes.valueOf(opCodeString);
    		resolveOpCode(opCode,value);
    	}
	}
}
